iT邦幫忙

2024 iThome 鐵人賽

DAY 15
0
Software Development

用 NestJS 闖蕩微服務!系列 第 15

[用NestJS闖蕩微服務!] DAY15 - Prometheus (下)

  • 分享至 

  • xImage
  •  

NestJS 與 Prometheus

使用 NestJS 要如何將相關監控資料提供給 Prometheus 抓取呢?有官方套件可以使用嗎?很遺憾的是,NestJS 官方並 沒有 提供 Prometheus 相關套件讓開發者使用,不過不需要擔心,Prometheus 官方有推薦第三方 Node.js 的 Client 函式庫 - prom-client 讓 Node.js 開發者可以透過它來定義基本的 Metrics 與 Labels,甚至有提供自訂 Metric 的功能,相當方便。

NestJS 社群開發者更是基於 prom-client 包裝了 nestjs-prometheus 的套件讓 NestJS 開發者可以用 NestJS 的風格來串接 Prometheus 的相關設定,大幅提升開發體驗。

安裝 Prometheus Module

透過下方指令安裝 nestjs-prometheus 與其依賴 prom-client

$ npm install @willsoto/nestjs-prometheus prom-client

使用 Prometheus Module

使用方式相當簡單,只需要引入 PrometheusModule 並使用其 registerregisterAsync 靜態方法,就會自動產生 /metrics Endpoint 並提供預設 Metrics。下方是範例程式碼:

import {Module } from '@nestjs/common';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
// ...

@Module({
  imports: [PrometheusModule.register()],
  // ...
})
export class AppModule {}

啟動應用程式後,透過 Postman 以 GET 方法存取 http://localhost:3000/metrics 就會得到 prom-client 預設提供的 Metrics:

Default Metrics Result1

補充:預設 Metrics 提供了部分 Prometheus 定義的 Metric,像是:process_cpu_seconds_total,除此之外,還提供了 Node.js 獨有的相關 Metrics,像是:nodejs_eventloop_lag_secondsnodejs_gc_duration_seconds_bucket 等,詳細資訊可以參考 官方文件

如果不想要啟用預設 Metrics,可以透過指定 defaultMetrics 中的 enabledfalse 來關閉。下方是範例程式碼:

import {Module } from '@nestjs/common';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
// ...

@Module({
  imports: [
    PrometheusModule.register({
      defaultMetrics: {
        enabled: false,
      },
    })
  ],
  // ...
})
export class AppModule {}

透過 Postman 以 GET 方法存取 http://localhost:3000/metrics 會發現預設 Metrics 的相關資料都不見了:

Default Metrics Result2

修改 Endpoint

如果 Prometheus 有特別設定抓取的 Endpoint 不是 /metrics,那可以透過 path 來指定特定的路徑。下方是範例程式碼,將本來的 /metrics 改成 /monitor

import {Module } from '@nestjs/common';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
// ...

@Module({
  imports: [
    PrometheusModule.register({
      path: '/monitor',
    })
  ],
  // ...
})
export class AppModule {}

透過 Postman 以 GET 方法存取 http://localhost:3000/monitor 會得到 Metrics 相關資料:

Prometheus Modify Path Result

自訂 Metric

nestjs-prometheus 有提供四個函式讓開發者可以自訂 Metric 並以 Provider 的形式讓 NestJS 管理實例:

  1. makeCounterProvider:用來定義 Counter 類型的 Metric。
  2. makeGaugeProvider:用來定義 Gauge 類型的 Metric。
  3. makeHistogramProvider:用來定義 Histogram 類型的 Metric。
  4. makeSummaryProvider:用來定義 Summary 類型的 Metric。

接下來實際做一個自訂 Metric 來收集 HTTP 請求數量,其名稱為 http_requests_total,並會以 HTTP Method、Endpoint Path 與 HTTP Code 作為 Label。下方是範例程式碼,在 providers 使用 makeCounterProvider 函式,並指定 namehttp_requests_totalhelpTotal number of HTTP requests 以及 labelNames['method', 'path', 'status']

import { Module } from '@nestjs/common';
import {
  makeCounterProvider,
  PrometheusModule,
} from '@willsoto/nestjs-prometheus';
// ...

@Module({
  imports: [PrometheusModule.register()],
  providers: [
    // ...
    makeCounterProvider({
      name: 'http_requests_total',
      help: 'Total number of HTTP requests',
      labelNames: ['method', 'path', 'status'],
    }),
  ],
  // ...
})
export class AppModule {}

為了方便我們把每一個 HTTP 請求都記錄下來,需要實作一個 RequestTotalMiddleware 讓所有請求都可以透過此 Middleware 來計數。下方是範例程式碼,透過 @InjectMetric 裝飾器帶入前面定義的 Metric 名稱 http_requests_total 來取得 Counter 實例,並在 use 方法中透過 Counterlabels 方法帶入 HTTP Method、Endpoint Path 與 HTTP Code 的值,須依照 labelNames 的順序帶入,最後再透過 inc 方法進行計數:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
import { InjectMetric } from '@willsoto/nestjs-prometheus';
import { Counter } from 'prom-client';

@Injectable()
export class RequestTotalMiddleware implements NestMiddleware {
  constructor(
    @InjectMetric('http_requests_total') private readonly counter: Counter,
  ) {}

  use(req: Request, res: Response, next: NextFunction) {
    this.counter.labels(req.method, req.path, res.statusCode.toString()).inc();
    next();
  }
}

補充:針對 NestJS Middleware 可以參考官方文件或是我之前分享的文章

最後,在 AppModule 中套用 RequestTotalMiddleware

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import {
  makeCounterProvider,
  PrometheusModule,
} from '@willsoto/nestjs-prometheus';
import { RequestTotalMiddleware } from './request-total.middleware';
// ...

@Module({
  imports: [PrometheusModule.register()],
  providers: [
    // ...
    makeCounterProvider({
      name: 'http_requests_total',
      help: 'Total number of HTTP requests',
      labelNames: ['method', 'path', 'status'],
    }),
  ],
  // ...
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(RequestTotalMiddleware).forRoutes('*');
  }
}

透過 Postman 進行測試,使用 GET 方法存取 http://localhost:3000/metrics 可以找到 http_requests_total 這個 Metric:

Custom Metric Result

整合 Prometheus

現在我們已經了解如何在 NestJS 提供 /metrics Endpoint 讓 Prometheus 抓取資料了,那就來進行整合吧!

首先,修改 prometheus.yml 的內容,新增一個 job_namenestjs-app 的 Job,並指定 targethost.docker.internal:3000 讓運行於 Docker 環境的 Prometheus 可以接觸到運行於本機的 NestJS 應用程式:

global:
  scrape_interval: 5s
scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "nestjs-app"
    static_configs:
      - targets: ["host.docker.internal:3000"]

接著,透過下方指令將 Prometheus 架設起來:

$ docker run --name <NAME> -p 9090:9090 -v /<FOLDER>:/etc/prometheus prom/prometheus

打開瀏覽器並存取 http://localhost:9090,在「Gragh」頁面上方的輸入框輸入 http_requests_total 就可以看到我們自訂的 Metric:

Prometheus GUI custom metric

小結

回顧一下今天的重點內容,雖然 NestJS 官方沒有提供 Prometheus 相關的套件給開發者使用,但 Node.js 與 NestJS 社群開發者貢獻了 prom-clientnestjs-prometheus 讓一切變得簡單,不僅提供了預設的 Metrics,還讓開發者可以自訂 Metric,甚至運用 nestjs-prometheus 可以讓我們使用 NestJS 的風格來進行開發,提升整體開發體驗!


上一篇
[用NestJS闖蕩微服務!] DAY14 - Prometheus (上)
下一篇
[用NestJS闖蕩微服務!] DAY16 - Logger
系列文
用 NestJS 闖蕩微服務!21
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言